<?php

// @formatter:off
/**
 * @file WCViewManager.php
 *
 * @author Alejandro Dario Simi
 * @date $Date: 2014-06-03 03:40:32 +0000 (Tue, 03 Jun 2014) $
 *
 * $Id: WCViewManager.php 112 2014-06-03 03:40:32Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/includes/mvc/WCViewManager.php $
 */
// @formatter:on

class WCViewManager extends WCSingleton {
	const ActionContentKeyword = "{{{ACTIONCONTENT}}}";
	const CacheTimeHalf = "half";
	const CacheTimeMax = "max";
	const CacheTimeShort = "short";
	const CacheTypeData = "D";
	const CacheTypeHTML = "H";
	const ControllerExtension = ".php";
	const DefaultAction = "404";
	const DefaultLayout = "index";
	const KeyGroup = "group";
	const KeyProfile = "profile";
	const KeyUser = "user";
	const KeyComic = "comic";
	const KeyImage = "image";
	const KeyCollection = "collection";
	const KeyTag = "tag";
	const KeyDirectory = "directory";
	const KeyRule = "rule";
	const KeyRuleAction = "rule_action";
	const SkinExtension = ".html";
	const ConfigExtension = ".json";
	//
	// Protected class properties.
	protected static $_Explain = array(
		"search" => array(),
		"class" => array()
	);
	//
	// Protected properties.
	protected $_action = array(
		"extension" => "",
		"name" => "",
		"path" => "",
		"skin" => ""
	);
	protected $_actionIds = false;
	protected $_cacheSelectors = array(
		"format" => "skin",
		"group" => 0,
		"profile" => "",
		"user" => 0,
		"comic" => 0,
		"image" => 0,
		"collection" => 0,
		"tag" => 0,
		"directory" => 0,
		"rule" => 0,
		"rule_action" => 0,
		"extra" => "",
		"page" => 1,
		"flow" => ""
	);
	protected $_constants = false;
	/**
	 * @var WCConnection Database pointer shortcut.
	 */
	protected $_db;
	/**
	 * @var string Database tables' prefix shortcut.
	 */
	protected $_dbprefix;
	protected $_layout = array(
		"extension" => "",
		"name" => "",
		"path" => "",
		"skin" => ""
	);
	protected $_requestMethod = false;
	protected $_settings = array();
	protected $_urlCacheSelectors = array(
		"format" => "skin",
		"profile" => "",
		"user" => 0,
		"extra" => ""
	);
	//
	// Magic methods.
	protected function __construct() {
		$this->_requestMethod = defined("__SHELL__") ? "GET" : $_SERVER["REQUEST_METHOD"];
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $this->_db->prefix();
	}
	public function __get($name) {
		return isset($this->_settings[$name]) ? $this->_settings[$name] : false;
	}
	public function __isset($name) {
		return isset($this->_settings[$name]);
	}
	public function __set($name, $value) {
		$this->_settings[$name] = $value;
		return $this->_settings[$name];
	}
	//
	// Public methods.
	public function actionId($name, $type = "A", $create = false) {
		$out = 0;
		//
		// Loading global pointers.
		global $wcSkin;

		$this->loadActionId();

		if(isset($this->_actionIds[$wcSkin][$type][$name])) {
			$out = $this->_actionIds[$wcSkin][$type][$name];
		} elseif($create) {
			$this->addActionId($name, $type);
			$out = $this->actionId($name, $type, false);
		}

		return $out;
	}
	public function buildOutput($action, $layout = false) {
		$output = "";

		if(isset($_REQUEST["explainsearch"])) {
			$output = WCViewManager::ExplainSearch();
		} elseif(isset($_REQUEST["explaincache"])) {
			$output = WCViewManager::ExplainCache($action, $layout);
		} else {
			$actionOutput = "";
			$layoutOutput = "";

			if($layout) {
				$layout->run();
				$layoutOutput = $layout->build();
			}
			if($action) {
				$action->run();
				$actionOutput = $action->build();
			}

			if(!$action && $layout && $layout->outputFormat() == WCViewController::OutputFormatSkin) {
				$output = str_replace(self::ActionContentKeyword, $actionOutput, $layoutOutput);
			} elseif($action && $action->outputFormat() == WCViewController::OutputFormatSkin && $layout && $layout->outputFormat() == WCViewController::OutputFormatSkin) {
				$output = str_replace(self::ActionContentKeyword, "\n<!-- START CONTENT @{ -->\n{$actionOutput}\n<!-- END CONTENT @} -->\n", $layoutOutput);
			} elseif(!$action && $layout) {
				$output = $layoutOutput;
			} else {
				$output = $actionOutput;
			}
		}

		return $output;
	}
	public function clean($isCron, $spacer) {
		//
		// Creating the statement to clean cache old data.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}mvc_cache\n";
			$query.= "where       mvcc_until < now()\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		$stmt->execute();
		//
		// Creating the statement to clean cache old invalid marks.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}mvc_invalids\n";
			$query.= "where       mvci_registered < (\n";
			$query.= "                    select min(mvcc_until)\n";
			$query = "                    from   {$this->_dbprefix}mvc_cache\n";
			$query.= "            )\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		$stmt->execute();

		$this->optimizeCache($isCron, $spacer);
	}
	public function constants() {
		if($this->_constants === false) {
			$constants = get_defined_constants(true);
			foreach($constants["user"] as $name => $value) {
				if(preg_match("/^WC_((ACTION)|(SACTION)|(EACTION)|(RSS)|(SERVICE)|(COOKIE))_/", $name)) {
					$this->_constants[$name] = $value;
				}
			}
		}

		return $this->_constants;
	}
	public function getCache($selector, $type, $fullUrl = false) {
		$out = false;
		//
		// Loading global pointers.
		global $wcSkinDefaults;
		//
		// Checking if skin cache system is activated.
		if($wcSkinDefaults["cache"]["active"] && !isset($_REQUEST["resetcache"])) {
			if($fullUrl) {
				$selector = array(
					"url" => md5($_SERVER["REQUEST_URI"]),
					"format" => $selector["format"]
				);
				$selector = $this->completeUrlCacheSelector($selector, $type);
				if($selector) {
					//
					// Creating the statement to get cache data.
					// @{
					$stmt = null;
					$stmtId = __CLASS__."::".__FUNCTION__."[FU]";
					if($this->_db->has($stmtId)) {
						$stmt = $this->_db->get($stmtId);
					} else {
						$query = "select  mvcc_data as data\n";
						$query.= "from    {$this->_dbprefix}mvc_urlcache\n";
						$query.= "where   mvcc_url  = :url\n";
						$query.= " and    mvcc_type = :type\n";
						foreach($this->_urlCacheSelectors as $name => $default) {
							$query.= " and    mvcc_{$name} = :{$name}\n";
						}

						$stmt = $this->_db->prepare($stmtId, $query);
					}
					// @}

					$params = array(
						":url" => $selector["url"],
						":type" => $type
					);
					foreach($this->_urlCacheSelectors as $name => $default) {
						$params[":{$name}"] = $selector[$name];
					}

					$this->cleanUrlCache($params);

					if($stmt->execute($params) && $stmt->rowCount() > 0) {
						$row = $stmt->fetch();
						$out = unserialize(gzuncompress($row["data"]));
					}
				}
			} else {
				$selector = $this->completeCacheSelector($selector, $type);
				if($selector) {
					$this->checkInvalids($selector);

					//
					// Creating the statement to get cache data.
					// @{
					$stmt = null;
					$stmtId = __CLASS__."::".__FUNCTION__;
					if($this->_db->has($stmtId)) {
						$stmt = $this->_db->get($stmtId);
					} else {
						$query = "select  mvcc_data as data\n";
						$query.= "from    {$this->_dbprefix}mvc_cache\n";
						$query.= "where   mvcc_action = :action\n";
						$query.= " and    mvcc_type   = :type\n";
						foreach($this->_cacheSelectors as $name => $default) {
							$query.= " and    mvcc_{$name} = :{$name}\n";
						}

						$stmt = $this->_db->prepare($stmtId, $query);
					}
					// @}

					$params = array(
						":action" => $selector["id"],
						":type" => $type
					);
					foreach($this->_cacheSelectors as $name => $default) {
						$params[":{$name}"] = $selector[$name];
					}

					$this->cleanCache($params);

					if($stmt->execute($params) && $stmt->rowCount() > 0) {
						$row = $stmt->fetch();
						$out = unserialize(gzuncompress($row["data"]));
					}
				}
			}
		}

		return $out;
	}
	public function invalidate($key, $id) {
		$ok = false;

		global $wcSkinDefaults;
		if($wcSkinDefaults["cache"]["invalids"]) {
			$ids = array();
			if(!is_array($id)) {
				$ids = array($id);
			} else {
				$ids = $id;
			}

			if(in_array($key, array_keys($this->_cacheSelectors))) {
				//
				// .
				// @{
				$stmt = null;
				$stmtId = __CLASS__."::".__FUNCTION__;
				if($this->_db->has($stmtId)) {
					$stmt = $this->_db->get($stmtId);
				} else {
					$query = "insert\n";
					$query.= "        into {$this->_dbprefix}mvc_invalids(mvci_key, mvci_id, mvci_registered)\n";
					$query.= "        values (:key, :id, now())\n";
					$query.= "                on duplicate key update\n";
					$query.= "                        mvci_registered = now()\n";

					$stmt = $this->_db->prepare($stmtId, $query);
				}
				// @}
				$params = array(
					":key" => $key
				);
				foreach($ids as $id) {
					$params[":id"] = $id;
					if($stmt->execute($params)) {
						$ok = true;
					}
				}
			}
		} else {
			$ok = true;
		}

		return $ok;
	}
	public function isGet() {
		return $this->_requestMethod == "GET";
	}
	public function isPost() {
		return $this->_requestMethod == "POST";
	}
	public function loadActionController($name = false) {
		$out = false;

		if(!isset($_REQUEST["noaction"])) {
			if($name === false) {
				if(isset($_REQUEST["action"])) {
					$name = $_REQUEST["action"];
				} else {
					global $wcSkinDefaults;

					if(defined("__MOBILE__")) {
						$name = $wcSkinDefaults["mobile-action"];
					} else {
						$name = $wcSkinDefaults["action"];
					}
				}

				$out = $this->loadActionController($name);
			} else {
				$out = $this->loadController("action", $name);
			}
		}

		return $out;
	}
	public function loadLayoutController($name = false) {
		$out = false;

		if(!isset($_REQUEST["nolayout"])) {
			if($name === false) {
				if(isset($_REQUEST["layout"])) {
					$name = $_REQUEST["layout"];
				} else {
					global $wcSkinDefaults;

					if(defined("__MOBILE__")) {
						$name = $wcSkinDefaults["mobile-layout"];
					} else {
						$name = $wcSkinDefaults["layout"];
					}
				}

				$out = $this->loadLayoutController($name);
			} else {
				$out = $this->loadController("layout", $name);
			}
		}

		return $out;
	}
	public function saveCache($selector, $type, $data, $expiration = self::CacheTimeMax, $fullUrl = false) {
		//
		// Loading global pointers.
		global $wcSkinDefaults;
		//
		// Checking if skin cache system is activated.
		if($wcSkinDefaults["cache"]["active"] && !isset($_REQUEST["nocache"])) {
			if($fullUrl) {
				$selector = array(
					"url" => md5($_SERVER["REQUEST_URI"]),
					"format" => $selector["format"]
				);
				$selector = $this->completeUrlCacheSelector($selector, $type);
				if($selector) {
					//
					// Creating the statement to save cache
					// data @{
					$stmt = null;
					$stmtId = __CLASS__."::".__FUNCTION__."[FU]";
					if($this->_db->has($stmtId)) {
						$stmt = $this->_db->get($stmtId);
					} else {
						$query = "insert\n";
						$query.= "        into {$this->_dbprefix}mvc_urlcache (\n";
						$query.= "                mvcc_url, mvcc_data, mvcc_until, mvcc_type";
						foreach($this->_urlCacheSelectors as $name => $default) {
							$query.= ", mvcc_{$name}";
						}
						$query.= ")\n";
						$query.= "        values (:url, :data, (now() + interval :until second), :type";
						foreach($this->_urlCacheSelectors as $name => $default) {
							$query.= ", :{$name}";
						}
						$query.= ")\n";
						$stmt = $this->_db->prepare($stmtId, $query);
					}
					// @}

					$params = array(
						":url" => $selector["url"],
						":type" => $type
					);
					foreach($this->_urlCacheSelectors as $name => $default) {
						$params[":{$name}"] = $selector[$name];
					}

					$this->cleanUrlCache($params, true);

					switch($expiration) {
						case self::CacheTimeShort:
							$params[":until"] = ceil($wcSkinDefaults["cache"]["expiration"] / 4);
							break;
						case self::CacheTimeHalf:
							$params[":until"] = ceil($wcSkinDefaults["cache"]["expiration"] / 2);
							break;
						case self::CacheTimeMax:
						default:
							$params[":until"] = $wcSkinDefaults["cache"]["expiration"];
							break;
					}
					$params[":data"] = gzcompress(serialize($data));

					$stmt->execute($params);
				}
			} else {
				$selector = $this->completeCacheSelector($selector, $type);
				if($selector) {
					//
					// Creating the statement to save cache
					// data @{
					$stmt = null;
					$stmtId = __CLASS__."::".__FUNCTION__;
					if($this->_db->has($stmtId)) {
						$stmt = $this->_db->get($stmtId);
					} else {
						$query = "insert\n";
						$query.= "        into {$this->_dbprefix}mvc_cache (\n";
						$query.= "                mvcc_action, mvcc_data, mvcc_until, mvcc_type";
						foreach($this->_cacheSelectors as $name => $default) {
							$query.= ", mvcc_{$name}";
						}
						$query.= ")\n";
						$query.= "        values (:action, :data, (now() + interval :until second), :type";
						foreach($this->_cacheSelectors as $name => $default) {
							$query.= ", :{$name}";
						}
						$query.= ")\n";

						$stmt = $this->_db->prepare($stmtId, $query);
					}
					// @}

					$params = array(
						":action" => $selector["id"],
						":type" => $type
					);
					foreach($this->_cacheSelectors as $name => $default) {
						$params[":{$name}"] = $selector[$name];
					}

					$this->cleanCache($params, true);

					switch($expiration) {
						case self::CacheTimeShort:
							$params[":until"] = ceil($wcSkinDefaults["cache"]["expiration"] / 4);
							break;
						case self::CacheTimeHalf:
							$params[":until"] = ceil($wcSkinDefaults["cache"]["expiration"] / 2);
							break;
						case self::CacheTimeMax:
						default:
							$params[":until"] = $wcSkinDefaults["cache"]["expiration"];
							break;
					}
					$params[":data"] = gzcompress(serialize($data));

					$stmt->execute($params);
				}
			}
		}
	}
	public function settings() {
		return $this->_settings;
	}
	//
	// Protected methods.
	protected function addActionId($name, $type) {
		//
		// Loading global pointers.
		global $wcSkin;
		//
		// Creating the statement to insert action ids.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "insert\n";
			$query.= "        into {$this->_dbprefix}mvc_actions(mvca_name, mvca_type, mvca_skin)\n";
			$query.= "        values (:name, :type, :skin)\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		$stmt->execute(array(
			":name" => $name,
			":type" => $type,
			":skin" => $wcSkin
		));

		$this->_actionIds = false;
	}
	protected function checkInvalids($selector) {
		global $wcSkinDefaults;

		if($wcSkinDefaults["cache"]["invalids"]) {
			$avoid = array(
				"id",
				"format",
				"group",
				"extra",
				"page",
				"flow"
			);
			//
			// @{
			$stmtSubId = __CLASS__."::".__FUNCTION__;
			$stmtG = null;
			$stmtD = null;
			$stmtGId = "{$stmtSubId}[GET]";
			$stmtDId = "{$stmtSubId}[DELETE]";
			if($this->_db->has($stmtGId)) {
				$stmtG = $this->_db->get($stmtGId);
				$stmtD = $this->_db->get($stmtDId);
			} else {
				$whereList = array();
				foreach($selector as $key => $value) {
					if(!in_array($key, $avoid)) {
						$whereList[] = "(mvci_key = '{$key}' and mvci_id = :{$key} and mvci_id <> '{$this->_cacheSelectors[$key]}')";
					}
				}

				$queryG = "select  *\n";
				$queryG.= "from    {$this->_dbprefix}mvc_invalids\n";
				$queryG.= "where   ".implode("\n or     ", $whereList);

				$stmtG = $this->_db->prepare($stmtGId, $queryG);

				$queryD = "delete from {$this->_dbprefix}mvc_invalids\n";
				$queryD.= "where   ".implode("\n or     ", $whereList);

				$stmtD = $this->_db->prepare($stmtDId, $queryD);
			}
			// @}
			$params = array();
			foreach($selector as $key => $value) {
				if(!in_array($key, $avoid)) {
					$params[":{$key}"] = $value;
				}
			}

			if($stmtG->execute($params) && $stmtG->rowCount() > 0) {
				foreach($stmtG->fetchAll() as $row) {
					//
					// @{
					$stmtU = null;
					$stmtIId = "{$stmtSubId}[{$row["mvci_key"]}]";
					if($this->_db->has($stmtIId)) {
						$stmtU = $this->_db->get($stmtIId);
					} else {
						$queryI = "update  {$this->_dbprefix}mvc_cache\n";
//						$queryI.= "set     mvcc_until = now()\n";
						$queryI.= "set     mvcc_until = 0\n";
						$queryI.= "where   mvcc_{$row["mvci_key"]} = :{$row["mvci_key"]}\n";

						$stmtU = $this->_db->prepare($stmtIId, $queryI);
					}
					// @}
					$stmtU->execute(array(":{$row["mvci_key"]}" => $row["mvci_id"]));
				}
				$stmtD->execute($params);
			}
		}
	}
	protected function cleanCache($selector, $forced = false) {
		//
		// Creating the statement to remove cache data.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__.($forced ? "[forced]" : "");
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}mvc_cache\n";
			$query.= "where       mvcc_action = :action\n";
			$query.= " and        mvcc_type   = :type\n";
			foreach($this->_cacheSelectors as $name => $default) {
				$query.= " and        mvcc_{$name} = :{$name}\n";
			}
			if(!$forced) {
				$query.= " and        mvcc_until  < now()\n";
			}

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		$stmt->execute($selector);
	}
	protected function cleanUrlCache($selector, $forced = false) {
		//
		// Creating the statement to remove cache data.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__.($forced ? "[forced]" : "");
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}mvc_urlcache\n";
			$query.= "where       mvcc_url  = :url\n";
			$query.= " and        mvcc_type = :type\n";
			foreach($this->_urlCacheSelectors as $name => $default) {
				$query.= " and        mvcc_{$name} = :{$name}\n";
			}
			if(!$forced) {
				$query.= " and        mvcc_until  < now()\n";
			}

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		$stmt->execute($selector);
	}
	protected function completeCacheSelector($selector, $type) {
		$out = false;

		if(isset($selector["id"])) {
			$out = $selector;

			if(isset($out["page"])) {
				$out["page"] = isset($_REQUEST["page"]) ? $_REQUEST["page"] : $this->_cacheSelectors["page"];
			}
			if(isset($out["flow"])) {
				$out["flow"] = isset($_REQUEST["flow"]) ? $_REQUEST["flow"] : $this->_cacheSelectors["flow"];
			}
			if($type == self::CacheTypeData) {
				$out["format"] = "data";
			}

			foreach($this->_cacheSelectors as $name => $default) {
				if(!isset($out[$name])) {
					$out[$name] = $default;
				}
			}
		}

		return $out;
	}
	protected function completeUrlCacheSelector($selector, $type) {
		$out = false;

		if(isset($selector["url"])) {
			$out = $selector;

			if($type == self::CacheTypeData) {
				$out["format"] = "data";
			}

			foreach($this->_urlCacheSelectors as $name => $default) {
				if(!isset($out[$name])) {
					$out[$name] = $default;
				}
			}
		}

		return $out;
	}
	protected function getFile($name, $extension, $isSkin = true, $isLayout = false, $isConfig = false, $isRecursive = false) {
		global $wcPaths;

		$out = false;

		$cleanName = str_replace(":", "/", $name);

		if($isSkin && !$isConfig && !$isRecursive) {
			global $wcSkin;
			$pieces = explode("/", $cleanName);
			$aux = array_pop($pieces);
			array_push($pieces, strtolower($wcSkin)."_{$aux}");
			$skinName = implode("/", $pieces);

			$out = $this->getFile($skinName, $extension, $isSkin, $isLayout, $isConfig, true);
		}

		if(!$out) {
			$where = ($isLayout ? "layouts" : "actions");
			$search = $where.($isConfig ? "-cnf" : "");
			$list = array();

			if($isSkin) {
				$search = "skin-{$search}";
				$list = $wcPaths["mvc"]["pools"]["skins"][$where];
			} else {
				$list = $wcPaths["mvc"]["pools"][$where];
			}

			if(!isset(self::$_Explain["search"][$search])) {
				self::$_Explain["search"][$search] = array();
			}
			if(!isset(self::$_Explain["search"][$search][$name])) {
				self::$_Explain["search"][$search][$name] = array();
			}

			foreach($list as $dirPath) {
				$path = WCSanitizer::SystemPath("{$dirPath}/{$cleanName}{$extension}");
				self::$_Explain["search"][$search][$name][$path] = false;
				if(is_readable($path)) {
					$out = $path;
					self::$_Explain["search"][$search][$name][$path] = true;
					break;
				}
			}
		}

		return $out;
	}
	protected function loadActionId() {
		if($this->_actionIds === false) {
			$this->_actionIds = array();
			//
			// Creating the statement to load actions.
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "select  *\n";
				$query.= "from    {$this->_dbprefix}mvc_actions\n";

				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}
			if($stmt->execute()) {
				foreach($stmt->fetchAll() as $row) {
					if(!isset($this->_actionIds[$row["mvca_skin"]])) {
						$this->_actionIds[$row["mvca_skin"]] = array(
							"A" => array(),
							"L" => array()
						);
					}
					$this->_actionIds[$row["mvca_skin"]][$row["mvca_type"]][$row["mvca_name"]] = $row["mvca_id"];
				}
			}
		}
	}
	protected function loadController($type, $name, $lastName = false) {
		$viewController = false;

		if($name != $lastName) {
			$httpError = WC_HTTPERROR_NOT_FOUND;
			$data = $this->loadControllerData($type, $name);

			$className = str_replace(" ", "", ucwords(strtolower("{$type} controller ".str_replace(array(":", "/", "."), " ", $name))));
			self::$_Explain["class"][$className] = false;

			if($data["path"]) {
				require_once $data["path"];

				if(class_exists($className)) {
					$viewConfig = $data["config"] ? json_decode(file_get_contents($data["config"])) : null;
					if(is_null($viewConfig)) {
						$viewConfig = new stdClass();
					}
					self::CompleteControllerConfig($viewConfig);

					self::$_Explain["class"][$className] = true;
					$viewController = new $className();
					$viewController->setConfig($viewConfig);
					$viewController->setParentView($lastName);

					$httpError = $viewController->check();
				}
			}

			global $wcSkinConfig;

			switch($httpError) {
				case WC_HTTPERROR_OK:
					$viewController->setSkin($data["skin"]);
					break;
				case WC_HTTPERROR_OK:
				case WC_HTTPERROR_BAD_REQUEST:
				case WC_HTTPERROR_UNAUTHORIZED:
				case WC_HTTPERROR_FORBIDDEN:
				case WC_HTTPERROR_INTERNAL_SERVER_ERROR:
				case WC_HTTPERROR_NOT_IMPLEMENTED:
					$viewController = $this->loadController($type, $httpError, $name);
					break;
				case WC_HTTPERROR_NOT_FOUND:
				default:
					if($type == "layout") {
						$viewController = $name != self::DefaultLayout ? $this->loadController($type, self::DefaultLayout, $name) : false;
					} else {
						if(isset($wcSkinConfig->not_implemented) && in_array($name, $wcSkinConfig->not_implemented)) {
							$viewController = $this->loadController($type, WC_HTTPERROR_NOT_IMPLEMENTED, $name);
						} else {
							$viewController = $name != self::DefaultAction ? $this->loadController($type, self::DefaultAction, $name) : false;
						}
					}
					break;
			}

			if(isset($_REQUEST["format"])) {
				$viewController->setOutputFormat($_REQUEST["format"]);
			}
		}

		return $viewController;
	}
	protected function loadControllerData($type, $name) {
		$data = array(
			"type" => $type,
			"name" => $name,
			"path" => "",
			"skin" => "",
			"config" => ""
		);

		switch($type) {
			case "action":
				$data["path"] = $this->getFile($name, self::ControllerExtension, false, false);
				$data["skin"] = $this->getFile($name, self::SkinExtension, true, false);
				$data["config"] = $this->getFile($name, self::ConfigExtension, true, false, true);
				if(!$data["config"]) {
					$data["config"] = $this->getFile($name, self::ConfigExtension, false, false, true);
				}
				break;
			case "layout":
				$data["path"] = $this->getFile($name, self::ControllerExtension, false, true);
				$data["skin"] = $this->getFile($name, self::SkinExtension, true, true);
				$data["config"] = $this->getFile($name, self::ConfigExtension, true, true, true);
				if(!$data["config"]) {
					$data["config"] = $this->getFile($name, self::ConfigExtension, false, true, true);
				}
				break;
		}

		return $data;
	}
	protected function optimizeCache($isCron, $spacer) {
		foreach(array("cache", "urlcache", "invalids") as $table) {
			//
			// Creating the statement to optimize a table @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__."[$table]";
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "optimize table {$this->_dbprefix}mvc_{$table}\n";

				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}

			$stmt->execute();
		}
	}
	//
	// Public class methods
	public static function CompleteControllerConfig(&$config, $recursionLevel = 0) {
		//
		// Master configuration: @{
		if(!isset($config->cache)) {
			$config->cache = new stdClass();
		}
		if(!isset($config->cache->duration)) {
			$config->cache->duration = false;
		}
		if(!isset($config->cache->keys)) {
			$config->cache->keys = array();
		}
		if(!isset($config->cache->fullurl)) {
			$config->cache->fullurl = false;
		}
		if(!$config->cache->fullurl && !count($config->cache->keys)) {
			$config->cache->duration = false;
		}
		if(!isset($config->params)) {
			$config->params = new stdClass();
		}
		if(!isset($config->params->required)) {
			$config->params->required = array();
		}
		if(!isset($config->params->allowed)) {
			$config->params->allowed = array();
		}
		if(!isset($config->post)) {
			$config->post = new stdClass();
		}
		if(!isset($config->post->params)) {
			$config->post->params = new stdClass();
		}
		if(!isset($config->post->params->required)) {
			$config->post->params->required = array();
		}
		if(!isset($config->post->params->allowed)) {
			$config->post->params->allowed = array();
		}
		// @}
		if(isset($config->config_on_params) && $recursionLevel < 2) {
			foreach($config->config_on_params as $paramConfig) {
				//
				// Ignoring wrong configuration.
				if(!isset($paramConfig->param) || !isset($paramConfig->value) || !isset($paramConfig->config)) {
					continue;
				}
				//
				// Ignoring configuration for non present
				// parameters.
				if(!isset($_REQUEST[$paramConfig->param])) {
					continue;
				}
				//
				// Ignoring parameters with non matching
				// values.
				if($paramConfig->value && $_REQUEST[$paramConfig->param] != $paramConfig->value) {
					continue;
				}
				//
				// Overwritting configurarion.
				$config = $paramConfig->config;
				self::CompleteControllerConfig($config, $recursionLevel + 1);
			}
		}
	}
	public static function ExplainCache(WCViewController &$action, WCViewController &$layout = null) {
		$html = "<pre>\n";

		if($action) {
			$html .= "Action Cache:\n";
			$html .= print_r($action->selector(), true);
			$html .= "\n";
		}
		if($layout) {
			$html .= "Layout Cache:\n";
			$html .= print_r($action->selector(), true);
			$html .= "\n";
		}

		$html .= "</pre>\n";

		return $html;
	}
	public static function ExplainSearch() {
		$html = "<pre>\n";

		$html .= "Path Searches:\n";
		$tops = array(
			"layouts" => "\tFront view layout:\n",
			"skin-layouts" => "\tSkin layout:\n",
			"actions" => "\tFront view action:\n",
			"skin-actions" => "\tSkin action:\n",
			"skin-layouts-cnf" => "\tLayout Config (in Skin):\n",
			"layouts-cnf" => "\tLayout Config (in Controllers):\n",
			"skin-actions-cnf" => "\tActions Config (in Skin):\n",
			"actions-cnf" => "\tActions Config (in Controllers):\n"
		);


		foreach($tops as $type => $title) {
			if(isset(self::$_Explain["search"][$type])) {
				$html .= $title;
				foreach(self::$_Explain["search"][$type] as $name => $files) {
					$html .= "\t\tFor '{$name}':\n";
					foreach($files as $file => $found) {
						$html .= "\t\t\t- '<span style='color:blue'>{$file}</span>': ".($found ? "<span style='color:green'>found</span>" : "<span style='color:red'>not found</span>")."\n";
					}
				}
				$html .= "\n";
			}
		}
		foreach(self::$_Explain["search"]as $type => $items) {
			if(!in_array($type, array_keys($tops))) {
				$html .= "\t{$type}:\n";
				foreach($items as $name => $files) {
					$html .= "\t\tFor '{$name}':\n";
					foreach($files as $file => $found) {
						$html .= "\t\t\t- '<span style='color:blue'>{$file}</span>': ".($found ? "<span style='color:green'>found</span>" : "<span style='color:red'>not found</span>")."\n";
					}
				}
				$html .= "\n";
			}
		}

		$html .= "\nClass Searches:\n";
		foreach(self::$_Explain["class"] as $className => $status) {
			$html .= "\tclass '<span style='color:blue'>{$className}</span>': ".($status ? "<span style='color:green'>found</span>" : "<span style='color:red'>not found</span>")."\n";
		}

		$html .= "</pre>\n";

		return $html;
	}
}
